#include "neolix_log.h"

#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#include <iostream>

#include <boost/filesystem.hpp>
#include "spdlog/fmt/ostr.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/spinlock_limit_time_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"

namespace ThreadLocalThreadSpdlogMsgBuf {}

namespace neodrive {
namespace neolog {

std::once_flag __init_flag;

NeoLog::NeoLog() {
  logger_->set_level(static_cast<spdlog::level::level_enum>(level_));
  logger_->set_pattern("%L[%C%m%d %H:%M:%S.%f][%t][%v");
}

std::string NeoLog::GetTimeWithFormat() {
  struct timespec ts;
  struct tm t;
  char buffer[time_translate_buffer_size_];
  clock_gettime(CLOCK_REALTIME, &ts);
  localtime_r(&(ts.tv_sec), &t);
  size_t size =
      strftime(buffer, time_translate_buffer_size_, "%Y%m%d-%H%M%S", &t);
  std::string ret(buffer, size);
  return ret + "." + std::to_string(ts.tv_nsec / 1000);
}

std::string NeoLog::GenerateLogFileName() {
  return log_dir_ + "/" + program_name_ + ".spd.log.INFO";
}

void NeoLog::Init() {
  InitLogDir(log_dir_);
  // spdlog::sink_ptr sink =
  //     std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
  //         GenerateLogFileName(), kMaxLogFileSize, kMaxFiles,
  // [this]() -> std::string {
  //   return log_dir_ + "/" + program_name_ + ".spd.log.INFO." +
  //          GetTimeWithFormat();
  //         });
  spdlog::sink_ptr sink =
      std::make_shared<spdlog::sinks::SpinLockLimitTimeSink>(
          GenerateLogFileName(), max_log_file_size_, 1,
          [log_dir = this->log_dir_, program_name = this->program_name_,
           this]() -> std::string {
            return log_dir + "/" + program_name + ".spd.log.INFO." +
                   GetTimeWithFormat();
          });
  logger_ = std::make_shared<spdlog::logger>(program_name_, sink);
  logger_->set_level(static_cast<spdlog::level::level_enum>(level_));
  logger_->set_pattern("%L[%C%m%d %H:%M:%S.%f][%t][%v");
  auto alsologtostderr_var = std::getenv("GLOG_alsologtostderr");
  if (alsologtostderr_var != nullptr) {
    auto alsologtostderr = std::stoi(std::string(alsologtostderr_var));
    if (alsologtostderr == 1) {
      auto console_sink =
          std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
      int console_sink_level = std::max(level_, LogLevel::INFO);
      console_sink->set_level(
          static_cast<spdlog::level::level_enum>(console_sink_level));
      console_sink->set_pattern("%^%l%$[%C%m%d %H:%M:%S.%f][%t][%v");
      logger_->sinks().push_back(console_sink);
      LOG_WARN("add stdout_color_sink_mt sink with level: {}",
               console_sink_level);
    }
  }
  LOG_WARN("Init log: {} {} {}", program_name_, log_dir_, level_);
}

void NeoLog::Destroy() { logger_.reset(); }

void NeoLog::InitLogDir(const std::string &log_path) {
  if (access(log_path.c_str(), F_OK) != -1) {
    return;
  }
  int status = boost::filesystem::create_directories(log_path.c_str());
  if (status == -1) {
    auto console = spdlog::stdout_color_mt("console");
    console->info("log dir init failed");
  }
}

NeoLog *NeoLog::Instance() {
  static NeoLog instance_;
  return &instance_;
}
}  // namespace neolog
}  // namespace neodrive
